//the Basic structure of valid Stream
//#define vstream_space_beg
//#define vstream_space_end

//#define blkSize
//#define vstream_per_area_limit
//#ifdef vstream_space_beg
#ifdef strType

#include "MutexLock.h"
#include "OSProc.h"

using namespace Interpreter;

#define vstream_space           (vstream_space_end - vstream_space_beg)
static constexpr _ptlong vstream_regnum = (vstream_per_area_limit / blkSize);
#define vstream_areanum         (vstream_space / vstream_per_area_limit)
#define perRegSize		(sizeof(_ptlong))

#define STRFUNC(funcName)  funcName


#define STREAM_TO_OFFSET(x)		((((_ptlong)(x)) - vstream_space_beg) >> PAGE_SIZE_OFFSET)
#define OFFSET_TO_STREAM(x)		((((_ptlong)(x)) << PAGE_SIZE_OFFSET) + vstream_space_beg)
#define NUM(x)                          ((_ptlong)(x))

volatile _mlock globalStreamLock = {0,0,0};		//need initialize


#if vstream_space > (vstream_area_limit)
#define MUTI_VSTREAM_AREA
#endif



#ifdef MUTI_VSTREAM_AREA
//Have muti area to manage blocks
//need to have a structure to record every area 's block used and find the best area to give block
#include "autoList.h"
//autoList is the one could always pop up the space with the most spare space
static autoList areas<vstream_regsize>([](_ptlong size)->ptlong*{return (_ptlong*)KMalloc(size);},vstream_areanum);
static streamLog<vstream_regnum>* perarea = NULL;

inline static _ptlong PopSpareBlock(_ptlong area = (~0))
{
  _ptlong offset = areas.PopArea(area);
  if(perarea == NULL)
    {
      perarea = KMalloc(sizeof(streamLog<vstream_regnum>) * vstream_areanum);
      for(_ptlong i = 0 ; i < vstream_areanum ; i++)
	perarea[i].perarea([](_ptlong* ptr,_ptlong bytes){_MemAll(0,ptr,bytes);});
    }

  offset = perarea[offset].PopRegOffset() * blkSize + offset * vstream_per_area_limit + vstream_space_begin;
  return offset;
}


inline static int PushUsedBlock(_ptlong vAddr)
{
  _ptlong areaoffset = (vAddr - vstream_space_begin)/vstream_per_area_limit ;
  _ptlong blkoffset  = ((vAddr - vstream_space_begin)%vstream_per_area_limit) / blkSize;
  area.PushArea(areaoffset);
  if(perarea == NULL)
    {
      perarea = KMalloc(sizeof(streamLog<vstream_regsize>) * vstream_areanum);
      for(_ptlong i = 0 ; i < vstream_areanum ; i++)
	perarea[i].perarea([](_ptlong* ptr,_ptlong bytes){_MemAll(0,ptr,bytes);});
    }
  perarea[areaoffset].PushRegOffset(blkoffset);
  return 1;
}
#define GetVStreamStatAddr(stream)  \
  (&(perarea[(NUM(stream) - vstream_space_beg)/vstream_area_limit].reg[(NUM(stream) - vstream_space_beg)%vstream_area_limit >> PAGE_SIZE_OFFSET]))
#define GetVStreamStat(stream)      (HWREG(GetVStreamStatAddr(stream)))

#else

static streamLog<vstream_regnum> perarea([](_ptlong* ptr,_ptlong bytes){_MemAll(0,ptr,bytes);});

inline static _ptlong PopSpareBlock(_ptlong area = (~0))
{
  return perarea.PopRegOffset()*blkSize + vstream_space_begin;
}



inline static int PushUsedBlock(_ptlong vAddr)
{
  perarea.PushRegOffset(((vAddr - vstream_area_begin)) / blkSize);
  return 1;
}

#define GetVStreamStatAddr(stream)  (&(perarea.reg[STREAM_TO_OFFSET(NUM(stream))]))
#define GetVStreamStat(stream)      (HWREG(GetVStreamStatAddr(stream)))
	
#endif

#define _INSERT(__member,_contain,__MAKENODE__,__INSERTNODE__) \
  (_member == NULL						\
   ? (_member = __MAKENODE__(_KMalloc(sizeof(_member)),_contain),1) \
   : (__INSERTNODE__(_member,__MAKENODE__(_KMalloc(sizeof(_member)),_contain)),1)) 

#define _REMOVE(_member,_contain,__REMOVENODE__)	\
  ({auto fun = [&fun](decltype(_member) node)->int	\
  {							\
    (node == NULL					\
    ? 0							\
    : ( ((decltype(_contain))(node->mark)) == _contain	\
	? (__REMOVENODE__(_member,node),_KFree(node),1)	\
	: fun(node->next)				\
	)						\
     );							\
  };fun();})


static inline strType ApplySpareBlock(_ptlong vAddr = (~0))
{
  // MUTEX_LOCK(&globalStreamLock);
  if(vAddr != (~0))
    vAddr = (vAddr - vstream_space_begin) / vstream_area_limit;
  strType ret =  PopSpareBlock(vAddr);
  //  MUTEX_UNLOCK(&globalStreamLock);
  return ret;
  
}

static strType _CreateStream(strType (*allocMemory)(_ptlong))
{
  MUTEX_LOCK(&globalStreamLock);
  strType ret =ApplySpareBlock();
  MUTEX_UNLOCK(&globalStreamLock);
  MUTEX_LOCK(&(NOWT->streamsLock));
  _INSERT(NOWT->streams,ret,MakeSListNode,InsertSListNode);
  MUTEX_UNLOCK(&(NOWT->streamsLock));
  return ret;
}

static strType _CreateStreamBlock(strType stream)
{
  MUTEX_LOCK(&globalStreamLock);
#ifdef MUTI_VSTREAM_AREA
  SET_STREAM_JUMPING_TO(stream,ApplySpareBlock(NUM(stream)));
#else
  SET_STREAM_JUMPING_TO(stream,ApplySpareBlock());  
#endif
  MUTEX_UNLOCK(&globalStreamLock);
  return stream;
}



static _ptlong _EndStream(strType stream)
{

  MUTEX_LOCK(&globalStreamLock);
  _ptlong ret = 0;
  MUTEX_LOCK(&(NOWT->streamsLock));
  if(_REMOVE(NOWT->streams,stream,RemoveSListNode) == 1)
    {
      ret = PushUsedBlock(stream);
    }
  MUTEX_UNLOCK(&(NOWT->streamsLock));
  MUTEX_UNLOCK(&globalStreamLock);
  return ret;
}







//#endif
#endif
//#endif
